package com.winphone.uvam.controller.file;

import com.winphone.uvam.common.BaseController;
import com.winphone.uvam.common.result.Result;
import com.winphone.uvam.entity.FileInfo;
import com.winphone.uvam.entity.SysUser;
import com.winphone.uvam.service.IFileInfoService;
import com.winphone.uvam.utils.OperateImageUtils;
import com.winphone.uvam.utils.SessionUtil;
import com.winphone.uvam.utils.image.ImageUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.activation.MimetypesFileTypeMap;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Paths;
import java.util.*;

/**
 * <p>
 * 文件上传 前端控制器
 * </p>
 *
 * @author LiaoJunJie
 * @since 2017-12-14
 */
@Controller
@RequestMapping({"/file","api/file"})
public class FileController extends BaseController {

    /**
     * windows 图片上传路径
     */
    @Value("${img.winLocation}")
    private String imgWinLocation;

    /**
     * liunx 临时图片上传路径
     */
    @Value("${img.linuxLocation}")
    private String imgLinuxLocation;

    /**
     * windows 文件上传目录
     */
    @Value("${file.winLocation}")
    private String fileWinLocation;

    /**
     * linux 文件上传目录
     */
    @Value("${file.linuxLocation}")
    private String fileLinuxLocation;

    @Value("${ueditor.uploadIp}")
    private String uploadIp;

    private final ResourceLoader resourceLoader;

    private static Logger logger = LoggerFactory.getLogger(FileController.class);

    @Autowired
    public FileController(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Bean
    MultipartConfigElement multipartConfigElement() {
        MultipartConfigFactory factory = new MultipartConfigFactory();
        factory.setLocation(getFileSaveLocationPath());
        return factory.createMultipartConfig();
    }

    @Autowired
    private IFileInfoService fileInfoService;

    /**
     * 文件下载
     *
     * @param md5       要下载资源文件的md5
     * @param name     自定义文件名，该文件名不能存在非法字符.如果此参数为空(null).将使用文件上传时的文件名
     * @param response
     * @param request
     * @return 下载结果, 在下载失败时, 将返回错误信息
     * @throws IOException 读写文件错误
     */
    @RequestMapping(value = "/download/{md5}")
    @ResponseBody
    public void downLoad(@PathVariable("md5") String md5,
                         @RequestParam(value = "name", required = false) String name,
                         HttpServletResponse response, HttpServletRequest request) throws IOException {
        //通过idOrMd5 查询该条记录是否存在
        FileInfo fileEntity = fileInfoService.getByMd5(md5);

        if (null == fileEntity) {
            logger.error("文件不存在");
        }
        //文件名
        String fileName = fileEntity.getName();

        //获取后缀
        String suffix = fileName.contains(".") ?
                fileName.substring(fileName.lastIndexOf("."), fileName.length()) : "";

        //获取contentType
        String contentType = fileEntity.getType() == null ?
                MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(fileName) : fileEntity.getType();

        //未自定义文件名，则使用上传时的文件名
        if (StringUtils.isEmpty(name)) {
            name = fileEntity.getName();
        }

        //如果未指定文件拓展名，则追加默认的文件拓展名
        if (!name.contains(".")) {
            name = name.concat(".").concat(suffix);
        }
        int skip = 0;
        long fSize = fileEntity.getSize();
        //尝试判断是否为断点下载
        try {
            //获取要继续下载的位置
            String Range = request.getHeader("Range").replace("bytes=", "").replace("-", "");
            skip = Integer.parseInt(Range);
        } catch (Exception ignore) {
        }

        response.setContentLength((int) fSize);//文件大小
        response.setContentType(contentType);
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(name, "utf-8"));
        //断点下载
        if (skip > 0) {
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
            String contentRange = "bytes " + skip + "-" + (fSize - 1) + "/" + fSize;
            response.setHeader("Content-Range", contentRange);
        }
        fileInfoService.writeFile(md5, response.getOutputStream(), getFileSaveLocationPath(), skip);

    }

    /**
     * 上传单个文件
     *
     * @param file 上传文件
     * @return 上传结果
     */
    @RequestMapping(value = "/upload-single")
    @ResponseBody
    public Object uploadFile(@RequestParam("file") MultipartFile file) {
        SysUser user = SessionUtil.getCurrentUser();
        String creator = user.getId().toString();
        String fileName = file.getOriginalFilename();
        String prefix=fileName.substring(fileName.lastIndexOf(".")+1);

        //保存
        FileInfo fileEntity;
        try {
            fileEntity = fileInfoService.saveFile(file.getInputStream(), getFileSaveLocationPath(), fileName,prefix,file.getContentType(),creator);
        } catch (IOException e) {
            throw new RuntimeException("save file error", e);
        }
        return renderSuccess(fileEntity);
    }


    /**
     * 上传文件,支持多文件上传.获取到文件流后,调用 saveFile(InputStream, String, String, String)}进行文件保存
     * 上传成功后,将返回资源信息如:[{"id":"fileId","name":"fileName","md5":"md5"}]
     *
     * @param files 上传的文件
     * @return 文件上传结果.
     */
    @RequestMapping(value = "/upload-multi")
    @ResponseBody
    public Object uploadFiles(@RequestParam("files") MultipartFile[] files) {
        if (null == files || files.length == 0) {
            return renderError();
        }
        List<FileInfo> fileInfoList = new LinkedList<>();
        //用户id
        String creator = SessionUtil.getCurrentUser().getId().toString();
        String fileSavePath = getFileSaveLocationPath();
        for (MultipartFile file : files) {
            String fileName = file.getOriginalFilename();
            //保存
            FileInfo fileInfo;
            try {
                fileInfo = fileInfoService.saveFile(file.getInputStream(), getFileSaveLocationPath(), fileSavePath, fileName, file.getContentType(), creator);
            } catch (IOException e) {
                throw new RuntimeException("save file error", e);
            }
            fileInfoList.add(fileInfo);
        }
        return renderSuccess(fileInfoList);
    }


    /**
     * 上传图片
     * @param file
     * @return
     */
    @RequestMapping(value = "/upload",method = RequestMethod.POST)
    @ResponseBody
    public Object uploadIMG(@RequestParam(value = "file", required = false) MultipartFile file) {
        StringBuilder fileNames = new StringBuilder();
        Map<String, String> map = new HashMap<>(1);
        if(null != file && !file.isEmpty()){
            //保存文件并生成缩略图
            Result uploadResult = OperateImageUtils.saveImage(file,getImgSaveLocationPath());
            if(uploadResult.isSuccess()){
                //获得图片名并保存
                fileNames.append(uploadResult.getObj()).append(",");
            }else {
                return renderError(uploadResult.getMsg(),Result.OTHER);
            }
            //保存所有图片名参数
            if (fileNames.length() > 1){
                fileNames.deleteCharAt(fileNames.length()-1);
                map.put("fileName",fileNames.toString());
            }
        }else{
            return renderError("上传图片为空",Result.OTHER);
        }
        return renderSuccess(map);
    }

    /**
     * 根据图片名称获取图片
     * @param filename
     * @return
     */
    @RequestMapping(method = RequestMethod.GET, value = "/getImage/{filename:.+}")
    @ResponseBody
    public ResponseEntity<?> getImage(@PathVariable String filename) {
        try {
            Resource r = resourceLoader.getResource("file:" + Paths.get(getImgSaveLocationPath(), filename).toString());
            return ResponseEntity.ok(resourceLoader.getResource("file:" + Paths.get(getImgSaveLocationPath(), filename).toString()));
        } catch (Exception e) {
            logger.error("getImage:{},EXCEPTION:{}",e.getMessage(),e);
            e.printStackTrace();
            return ResponseEntity.notFound().build();
        }
    }

    /**
     * 根据图片名称删除图片
     * @param filename
     * @return
     */
    @RequestMapping(value = "/deleteFile",method = RequestMethod.POST)
    @ResponseBody
    public Object deleteFile(@RequestParam("filename") String filename) {
        try {
            File file = new File(getImgSaveLocationPath(),filename);
            if(file.exists()){
                file.delete();
            }else{
                return renderError("文件不存在", Result.OTHER);
            }
        }catch (Exception e){
            return renderError();

        }
        return renderSuccess();
    }

    /**
     * 视频流读取
     * @param md5
     * @param response
     * @throws Exception
     */
    @RequestMapping(value = "/video/{md5:.+}",method = RequestMethod.GET)
    public @ResponseBody void video(@PathVariable String md5, HttpServletResponse response){
        //根据id和路径读取文件流
        InputStream in = null;
        ServletOutputStream out = null;
        try {
            in = fileInfoService.readFile(md5,getFileSaveLocationPath());
            out = response.getOutputStream();
            byte[] buffer = new byte[4 * 1024];
            int length;
            while ((length = in.read(buffer)) > 0) {
                out.write(buffer, 0, length);
            }
        } catch (FileNotFoundException e) {
            logger.error("文件读取失败,文件不存在");
        } catch (IOException e) {
            logger.error("文件流输出异常");
        } finally {
            try {
                in.close();
                out.close();
            } catch (IOException e) {
                logger.error("文件流关闭异常");
            }
        }
    }

    /**
     *  获取当前系统的图片上传路径，windows 和liunx 进行区分
     * @return
     */
    public String getImgSaveLocationPath(){
        if (currentOSName().startsWith("windows")){
            return imgWinLocation;
        } else{
            return imgLinuxLocation;
        }
    }

    /**
     *  获取当前系统的文件上传路径，windows 和liunx 进行区分
     * @return
     */
    public String getFileSaveLocationPath() {
        if (currentOSName().startsWith("windows")) {
            return fileWinLocation;
        } else {
            return fileLinuxLocation;
        }
    }

    /**
     * 获取当前操作系统
     * @return
     */
    private static String currentOSName(){
        String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.US);
        return OS_NAME;
    }

    /**
     * 获取配置文件，如果使用jsp则不需要此接口
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(value = "/ueditor")
    @ResponseBody
    public String ueditor(HttpServletRequest request, HttpServletResponse response) {

        String s = "{\n"+
                "            \"imageActionName\": \"uploadimage\",\n" +
                "                \"imageFieldName\": \"file\", \n" +
                "                \"imageMaxSize\": 3080000, \n" +
                "                \"imageAllowFiles\": [\".png\", \".jpg\", \".jpeg\", \".gif\", \".bmp\"], \n" +
                "                \"imageCompressEnable\": true, \n" +
                "                \"imageCompressBorder\": 1600, \n" +
                "                \"imageInsertAlign\": \"none\", \n" +
                "                \"imageUrlPrefix\": \"\",\n" +
                "                \"imagePathFormat\": \"/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}\" }";
        return s;

    }

    /**
     * 富文本图片上传
     * @param file
     * @return
     */
    @RequestMapping(value = "/imgUpdate")
    @ResponseBody
    public String imgUpdate(MultipartFile file) {
        if (file.isEmpty()) {
            return "error";
        }
        // 获取文件名
        String fileName = file.getOriginalFilename();
        // 获取文件的后缀名
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        // 这里我使用随机字符串来重新命名图片
        fileName = UUID.randomUUID().toString() + suffixName;
        // 这里的路径为Nginx的代理路径，这里是/data/images/xxx.png
        File dest = new File(imgWinLocation + fileName);
        File destScale = new File(imgWinLocation + UUID.randomUUID().toString() + suffixName);

        // 检测是否存在目录
        if (!dest.getParentFile().exists()) {
            dest.getParentFile().mkdirs();
        }
        // 检测是否存在目录
        if (!destScale.getParentFile().exists()) {
            dest.getParentFile().mkdirs();
        }

        try {
            file.transferTo(dest);
            ImageUtils.reduceImg(dest, destScale, 0, 0,0.5f);

            OperateImageUtils.deleteFile(dest);

            String u = uploadIp+"/api/file/getImage/"+destScale.getName();
            //url的值为图片的实际访问地址 这里我用了Nginx代理，访问的路径是http://localhost/xxx.png
            String config = "{\"state\": \"SUCCESS\"," +
                    "\"url\": \"" + u + "\"," +
                    "\"title\": \"" + fileName + "\"," +
                    "\"original\": \"" + fileName + "\"}";
            return config;
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "error";
    }

}
